package cn.iocoder.yudao.module.picturebook.service.minio;


import cn.iocoder.yudao.module.picturebook.dal.dataobject.minio.FileInfo;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
public class MinioServiceImpl implements MinioService {

    @Autowired
    private MinioClient minioClient;

    @Value("${minio.endpoint}")
    private String endpoint;

    @Value("${minio.bucketName}")
    private String defaultBucketName;

    @Override
    public void createBucket(String bucketName) {
        try {
            if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
            }
        } catch (Exception e) {
            throw new RuntimeException("创建存储桶失败", e);
        }
    }

    @Override
    public boolean bucketExists(String bucketName) {
        try {
            return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            throw new RuntimeException("检查存储桶失败", e);
        }
    }

    @Override
    public void removeBucket(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            throw new RuntimeException("删除存储桶失败", e);
        }
    }

    @Override
    public List<String> getAllBuckets() {
        try {
            List<Bucket> bucketList = minioClient.listBuckets();
            List<String> bucketNames = new ArrayList<>();
            for (Bucket bucket : bucketList) {
                bucketNames.add(bucket.name());
            }
            return bucketNames;
        } catch (Exception e) {
            throw new RuntimeException("获取存储桶列表失败", e);
        }
    }

    @Override
    public FileInfo uploadFile(MultipartFile file, String bucketName) {
        if (file == null || file.isEmpty()) {
            throw new RuntimeException("上传文件不能为空");
        }

        if (bucketName == null || bucketName.isEmpty()) {
            bucketName = defaultBucketName;
        }

        try {
            // 创建存储桶
            createBucket(bucketName);

            // 生成文件名
            String originalFilename = file.getOriginalFilename();
            assert originalFilename != null;
            String fileName = generateFileName(originalFilename);

            // 获取文件类型
            String contentType = file.getContentType();
            if (contentType == null) {
                contentType = "application/octet-stream";
            }

            // 关键修复：使用try-with-resources自动关闭输入流，确保文件句柄释放
            try (InputStream inputStream = file.getInputStream()) { // 自动关闭流
                // 上传文件（使用try-with-resources中的inputStream）
                minioClient.putObject(PutObjectArgs.builder()
                        .bucket(bucketName)
                        .object(fileName)
                        .contentType(contentType)
                        .stream(inputStream, file.getSize(), -1)
                        .build());
            } // 此处自动执行inputStream.close()，无论是否异常

            // 构建文件信息
            FileInfo fileInfo = new FileInfo();
            fileInfo.setFileName(fileName);
            fileInfo.setFileUrl(getFileUrl(bucketName, fileName));
            fileInfo.setContentType(contentType);
            fileInfo.setSize(file.getSize());
            fileInfo.setUploadDate(new Date());
            fileInfo.setBucketName(bucketName);

            return fileInfo;
        } catch (Exception e) {
            throw new RuntimeException("上传文件失败", e);
        }
    }
    @Override
    public FileInfo uploadFile(String fileName, String contentType, InputStream stream, String bucketName) {
        if (fileName == null || fileName.isEmpty()) {
            throw new RuntimeException("文件名不能为空");
        }

        if (stream == null) {
            throw new RuntimeException("文件流不能为空");
        }

        if (bucketName == null || bucketName.isEmpty()) {
            bucketName = defaultBucketName;
        }

        try {
            // 创建存储桶
            createBucket(bucketName);

            // 生成文件名
            String generatedFileName = generateFileName(fileName);

            if (contentType == null || contentType.isEmpty()) {
                contentType = "application/octet-stream";
            }

            // 获取文件大小
            long size = stream.available();

            // 上传文件
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(generatedFileName)
                    .contentType(contentType)
                    .stream(stream, size, -1)
                    .build());

            // 构建文件信息
            FileInfo fileInfo = new FileInfo();
            fileInfo.setFileName(generatedFileName);
            fileInfo.setFileUrl(getFileUrl(bucketName, generatedFileName));
            fileInfo.setContentType(contentType);
            fileInfo.setSize(size);
            fileInfo.setUploadDate(new Date());
            fileInfo.setBucketName(bucketName);

            return fileInfo;
        } catch (Exception e) {
            throw new RuntimeException("上传文件失败", e);
        } finally {
            try {
                if (stream != null) {
                    stream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public InputStream getObject(String bucketName, String objectName) {
        try {
            return minioClient.getObject(GetObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .build());
        } catch (Exception e) {
            throw new RuntimeException("获取文件失败", e);
        }
    }

    @Override
    public void removeObject(String bucketName, String objectName) {
        try {
            minioClient.removeObject(RemoveObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .build());
        } catch (Exception e) {
            throw new RuntimeException("删除文件失败", e);
        }
    }

    @Override
    public FileInfo getFileInfo(String bucketName, String objectName) {
        try {
            StatObjectResponse stat = minioClient.statObject(StatObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .build());

            FileInfo fileInfo = new FileInfo();
            fileInfo.setFileName(stat.object());
            fileInfo.setFileUrl(getFileUrl(bucketName, objectName));
            fileInfo.setContentType(stat.contentType());
            fileInfo.setSize(stat.size());
            fileInfo.setUploadDate(Date.from(stat.lastModified().toInstant()));
            fileInfo.setBucketName(bucketName);

            return fileInfo;
        } catch (Exception e) {
            throw new RuntimeException("获取文件信息失败", e);
        }
    }

    @Override
    public String getPresignedObjectUrl(String bucketName, String objectName, Integer expires) {
        try {
            if (expires == null || expires <= 0) {
                expires = 7 * 24 * 60 * 60; // 默认7天
            }

            return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .method(Method.GET)
                    .bucket(bucketName)
                    .object(objectName)
                    .expiry(expires)
                    .build());
        } catch (Exception e) {
            throw new RuntimeException("获取文件URL失败", e);
        }
    }

    /**
     * 生成文件名，避免重复
     * @param originalFilename 原始文件名
     * @return 生成的文件名
     */
    private String generateFileName(String originalFilename) {
        // 获取文件扩展名
        String ext = originalFilename.substring(originalFilename.lastIndexOf("."));

        // 生成日期目录
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        String dateDir = sdf.format(new Date());

        // 生成唯一文件名
        String uuid = UUID.randomUUID().toString().replace("-", "");

        return dateDir + "/" + uuid + ext;
    }

    /**
     * 获取文件URL
     * @param bucketName 存储桶名称
     * @param objectName 对象名称
     * @return 文件URL
     */
    private String getFileUrl(String bucketName, String objectName) {
        // 去除endpoint的末尾斜杠
        String cleanEndpoint = endpoint.endsWith("/") ? endpoint.substring(0, endpoint.length() - 1) : endpoint;
        return cleanEndpoint + "/" + bucketName + "/" + objectName;
    }
}    